<?php
declare(strict_types=1);

namespace LowCode;

use BadMethodCallException;
use Closure;
use LowCode\Support\Fluent;
use LowCode\Traits\HasMacroable;
use LowCode\Traits\HasMake;

/**
 * @property string $name
 * @property string $path
 * @property string $component
 * @property string $redirect
 * @property bool|array $props
 * @property array $meta
 * @method $this title(string $value) 用于配置页面的标题，会在菜单和标签页中显示。一般会配合国际化使用。
 * @method $this icon(string $value) 用于配置页面的图标，会在菜单和标签页中显示。一般会配合图标库使用，如果是http链接，会自动加载图片。
 * @method $this activeIcon(string $value) 用于配置页面的激活图标，会在菜单中显示。一般会配合图标库使用，如果是http链接，会自动加载图片。
 * @method $this keepAlive(bool $bool) 用于配置页面是否开启缓存，开启后页面会缓存，不会重新加载，仅在标签页启用时有效。
 * @method $this hideInMenu(bool $bool) 用于配置页面是否在菜单中隐藏，隐藏后页面不会在菜单中显示。
 * @method $this hideInTab(bool $bool) 用于配置页面是否在标签页中隐藏，隐藏后页面不会在标签页中显示。
 * @method $this hideInBreadcrumb(bool $bool) 用于配置页面是否在面包屑中隐藏，隐藏后页面不会在面包屑中显示。
 * @method $this hideChildrenInMenu(bool $bool) 用于配置页面的子页面是否在菜单中隐藏，隐藏后子页面不会在菜单中显示。
 * @method $this authority(string[] $value) 用于配置页面的权限，只有拥有对应权限的用户才能访问页面，不配置则不需要权限。
 * @method $this badge(string $value) 用于配置页面的徽标，会在菜单显示。
 * @method $this badgeType(string $value = 'dot' | 'normal') 用于配置页面的徽标类型，dot 为小红点，normal 为文本。
 * @method $this badgeVariants(string $value = 'default' | 'destructive' | 'primary' | 'success' | 'warning') 用于配置页面的徽标颜色。
 * @method $this activePath(string $value) 用于配置当前激活的菜单，有时候页面没有显示在菜单内，需要激活父级菜单时使用。
 * @method $this affixTab(bool $bool) 用于配置页面是否固定标签页，固定后页面不可关闭。
 * @method $this affixTabOrder(int $value) 用于配置页面固定标签页的排序, 采用升序排序。
 * @method $this iframeSrc(string $value) 用于配置内嵌页面的 iframe 地址，设置后会在当前页面内嵌对应的页面。
 * @method $this ignoreAccess(bool $bool) 用于配置页面是否忽略权限，直接可以访问。
 * @method $this link(string $value) 用于配置外链跳转路径，会在新窗口打开。
 * @method $this maxNumOfOpenTab(int $value) 用于配置标签页最大打开数量，设置后会在打开新标签页时自动关闭最早打开的标签页(仅在打开同名标签页时生效)。默认 -1
 * @method $this menuVisibleWithForbidden(bool $bool) 用于配置页面在菜单可以看到，但是访问会被重定向到403。
 * @method $this openInNewWindow(bool $bool) 在新窗口打开
 * @method $this order(int $value) 用于配置页面的排序，用于路由到菜单排序。
 * @method static static make(string $title,string $name, string $path, string $component = null) 创建节点
 */
class MenuRouter extends Fluent
{
    private static array $allowAttributes = [
        "title","icon","activeIcon","keepAlive","hideInMenu","hideInTab","hideInBreadcrumb","hideChildrenInMenu",
        "authority","badge","badgeType","badgeVariants","activePath","affixTab","affixTabOrder","iframeSrc",
        "ignoreAccess","link","maxNumOfOpenTab","menuVisibleWithForbidden","openInNewWindow","order"
    ];

    use HasMake, HasMacroable;

    /**
     * @param string $title 路由标题
     * @param string $name 路由名称
     * @param string $path 路由路径
     * @param string|null $component 路由组件
     */
    public function __construct(string $title,string $name, string $path, string $component = null)
    {
        parent::__construct(['name'=>$name,'path'=>$path]);
        $this->title($title);
        if ($component){
            $this->component($component);
        }
    }

    /**
     * @param string $title
     * @param string $name
     * @param string $icon
     * @return static
     */
    public static function menu(string $title, string $name, string $icon = ""): static
    {
        return self::make($name,"/$name", $name)->title($title)->icon($icon);
    }

    /**
     * @param string $name 路由名称
     * @return $this
     */
    public function name(string $name): static
    {
        $this->name = $name;
        return $this;
    }

    /**
     * @param string $path 路由路径
     * @return $this
     */
    public function path(string $path): static
    {
        $this->path = $path;
        return $this;
    }

    /**
     * @param string $component 路由组件
     * @return $this
     */
    public function component(string $component): static
    {
        $this->component = $component;
        return $this;
    }

    /**
     * @param string $redirect 路由跳转
     * @return $this
     */
    public function redirect(string $redirect): static
    {
        $this->redirect = $redirect;
        return $this;
    }

    /**
     * @param bool|array $props
     * @return $this
     */
    public function props(bool|array $props): static
    {
        $this->props = $props;
        return $this;
    }

    /**
     * @param array $meta
     * @return $this
     */
    public function meta(array $meta): static
    {
        $this->meta = $meta;
        return $this;
    }

    /**
     * @param string $key
     * @param mixed $value
     * @return $this
     */
    private function setMeta(string $key, mixed $value): static
    {
        $meta = $this->get("meta",[]);
        $meta[$key] = $value;
        return $this->meta($meta);
    }

    /**
     * @param MenuRouter|MenuRouter[] $builder
     * @return $this
     */
    public function children(MenuRouter|array|Closure $builder): static
    {
        $options = $this->get("children",[]);
        if (is_array($builder)){
            $options = array_merge($options, $builder);
        }else if ($builder instanceof Closure){
            call_user_func($builder, $this);
        }else{
            $options[] = $builder;
        }
        return $this->setAttribute("children", $options);
    }

    public function __call($method, $parameters)
    {
        $keys = array_flip(self::$allowAttributes);
        if (isset($keys[$method])){
            return $this->setMeta($method, ...$parameters);
        }
        if (! static::hasMacro($method)) {
            throw new BadMethodCallException(sprintf(
                'Method %s::%s does not exist.', static::class, $method
            ));
        }

        $macro = static::$macros[$method];

        if ($macro instanceof Closure) {
            $macro = $macro->bindTo($this, static::class);
        }

        return $macro(...$parameters);
    }

}
